package org.femto.compmod.vis;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import com.sun.opengl.util.*;

import org.femto.math.Util;
import org.femto.math.linalg.*;

public class Drawing {
    private static GLU glu = new GLU();

    public static void drawBasis(GL gl) {
        gl.glColor3f(1.0f, 0.0f, 0.0f);
        gl.glBegin(GL.GL_LINES);
        gl.glVertex3f(0.0f, 0.0f, 0.0f);
        gl.glVertex3f(1.0f, 0.0f, 0.0f);
        gl.glEnd();

        gl.glColor3f(0.0f, 1.0f, 0.0f);
        gl.glBegin(GL.GL_LINES);
        gl.glVertex3f(0.0f, 0.0f, 0.0f);
        gl.glVertex3f(0.0f, 1.0f, 0.0f);
        gl.glEnd();

        gl.glColor3f(0.0f, 0.0f, 1.0f);
        gl.glBegin(GL.GL_LINES);
        gl.glVertex3f(0.0f, 0.0f, 0.0f);
        gl.glVertex3f(0.0f, 0.0f, 1.0f);
        gl.glEnd();
    }

    public static void drawCircle(GL gl, double x, double y, double r, int segments) {
        gl.glBegin(GL.GL_LINE_LOOP);
        for (int i = 0; i < segments; i++) {
            double c = r * Math.cos((double) i * 2.0 * Math.PI / (double) segments);
            double s = r * Math.sin((double) i * 2.0 * Math.PI / (double) segments);

            gl.glVertex2d(x + c, y + s);
        }
        gl.glEnd();
    }

    public static void drawCircularArrows(GL gl, double x, double y, double r, double arrowsize,
            double narrowness, int segments) {
        for (int i = 0; i < segments; i++) {
            // Point on the circle:
            double c = r * Math.cos((double) i * 2.0 * Math.PI / (double) segments);
            double s = r * Math.sin((double) i * 2.0 * Math.PI / (double) segments);

            // Derivative of that point:
            double dcdi = -2.0 * Math.PI / (double) segments * r * r * Math.sin((double) i * 2.0 * Math.PI / (double) segments);
            double dsdi = 2.0 * Math.PI / (double) segments * r * r * Math.cos((double) i * 2.0 * Math.PI / (double) segments);

            gl.glBegin(GL.GL_LINE_STRIP);
            gl.glVertex3d(x + c + dcdi * arrowsize, y + s + dsdi * arrowsize, -arrowsize * r * narrowness);
            gl.glVertex3d(x + c, y + s, 0.0);
            gl.glVertex3d(x + c + dcdi * arrowsize, y + s + dsdi * arrowsize, arrowsize * r * narrowness);
            gl.glEnd();
        }
    }

    public static void drawDisk(GL gl, double x, double y, double r, int segments) {
        gl.glBegin(GL.GL_TRIANGLE_FAN);
        gl.glVertex2d(x, y);
        for (int i = 0; i < segments + 1; i++) {
            double c = r * Math.cos((double) i * 2.0 * Math.PI / (double) segments);
            double s = r * Math.sin((double) i * 2.0 * Math.PI / (double) segments);

            gl.glVertex2d(x + c, y + s);
        }
        gl.glEnd();
    }

    public static void drawRing(GL gl, double x, double y, double r, double R, int segments) {
        gl.glBegin(GL.GL_TRIANGLE_STRIP);
        for (int i = 0; i < segments + 1; i++) {
            double c = r * Math.cos((double) i * 2.0 * Math.PI / (double) segments);
            double s = r * Math.sin((double) i * 2.0 * Math.PI / (double) segments);
            double cc = R * Math.cos((double) (i + 1) * 2.0 * Math.PI / (double) segments);
            double sc = R * Math.sin((double) (i + 1) * 2.0 * Math.PI / (double) segments);

            gl.glVertex2d(x + c, y + s);
            if (i < segments + 1) {
                gl.glVertex2d(x + cc, y + sc);
            }
        }

        gl.glEnd();
    }

    public static void drawGroundGrid(GL gl, int n, int m) {
        gl.glBegin(GL.GL_LINES);
        for (int i = 0; i < n + 1; i++) {
            gl.glVertex2d((double) i / (double) n, 0);
            gl.glVertex2d((double) i / (double) n, 1);
        }
        for (int j = 0; j < m + 1; j++) {
            gl.glVertex2d(0, (double) j / (double) m);
            gl.glVertex2d(1, (double) j / (double) m);
        }
        gl.glEnd();
    }

    public static void drawInfiniteGroundGrid(GL gl) {
        final double size = 1000;
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPushMatrix();
        gl.glScaled(size, size, 0);
        gl.glTranslated(-0.5, -0.5, 0);
        drawGroundGrid(gl, (int) size, (int) size);
        gl.glPopMatrix();
    }

    public static void drawScreenBox(GL gl, int boxHalfSide, Vec3 color) {
        double[] projection = new double[16];
        double[] modelview = new double[16];
        int[] viewport = new int[4];
        double[] center = new double[3];
        int i;

        gl.glGetDoublev(GL.GL_PROJECTION_MATRIX, projection, 0);
        gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, modelview, 0);
        gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
        
        glu.gluProject(0, 0, 0, modelview, 0, projection, 0, viewport, 0, center, 0);

        Vec3[] vertices = { new Vec3(boxHalfSide,boxHalfSide,0),
                            new Vec3(-boxHalfSide,boxHalfSide,0),
                            new Vec3(-boxHalfSide,-boxHalfSide,0),
                            new Vec3(boxHalfSide,-boxHalfSide,0) };

        Matrix4x4 projGUI = new Matrix4x4();
        projGUI.setOrthographic(0, viewport[2], 0, viewport[3], -1, 1);

        gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
        gl.glPolygonOffset(1, 1);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPushMatrix();
        gl.glLoadMatrixd(projGUI.m, 0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPushMatrix();
        gl.glLoadIdentity();
        gl.glTranslated(center[0], center[1], center[2]);

        gl.glBegin(GL.GL_LINE_LOOP);
        gl.glColor3dv(color.v, 0);
        for(i = 0; i < 4; i++) {
            gl.glVertex3dv(vertices[i].v, 0);
        }
        gl.glEnd();
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPopMatrix();
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPopMatrix();

        gl.glDisable(GL.GL_POLYGON_OFFSET_FILL);
    }
}
